home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / com32 / lib / vsnprintf.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-11-10  |  9.4 KB  |  434 lines

  1. /*
  2.  * vsnprintf.c
  3.  *
  4.  * vsnprintf(), from which the rest of the printf()
  5.  * family is built
  6.  */
  7.  
  8. #include <stdarg.h>
  9. #include <stddef.h>
  10. #include <inttypes.h>
  11. #include <string.h>
  12. #include <limits.h>
  13. #include <stdio.h>
  14.  
  15. enum flags {
  16.   FL_ZERO   = 0x01,        /* Zero modifier */
  17.   FL_MINUS  = 0x02,        /* Minus modifier */
  18.   FL_PLUS   = 0x04,        /* Plus modifier */
  19.   FL_TICK   = 0x08,        /* ' modifier */
  20.   FL_SPACE  = 0x10,        /* Space modifier */
  21.   FL_HASH   = 0x20,        /* # modifier */
  22.   FL_SIGNED = 0x40,        /* Number is signed */
  23.   FL_UPPER  = 0x80        /* Upper case digits */
  24. };
  25.  
  26. /* These may have to be adjusted on certain implementations */
  27. enum ranks {
  28.   rank_char    = -2,
  29.   rank_short    = -1,
  30.   rank_int     = 0,
  31.   rank_long    = 1,
  32.   rank_longlong    = 2
  33. };
  34.  
  35. #define MIN_RANK    rank_char
  36. #define MAX_RANK    rank_longlong
  37.  
  38. #define INTMAX_RANK    rank_longlong
  39. #define SIZE_T_RANK    rank_long
  40. #define PTRDIFF_T_RANK    rank_long
  41.  
  42. #define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
  43.  
  44. static size_t
  45. format_int(char *q, size_t n, uintmax_t val, enum flags flags,
  46.        int base, int width, int prec)
  47. {
  48.   char *qq;
  49.   size_t o = 0, oo;
  50.   static const char lcdigits[] = "0123456789abcdef";
  51.   static const char ucdigits[] = "0123456789ABCDEF";
  52.   const char *digits;
  53.   uintmax_t tmpval;
  54.   int minus = 0;
  55.   int ndigits = 0, nchars;
  56.   int tickskip, b4tick;
  57.  
  58.   /* Select type of digits */
  59.   digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
  60.  
  61.   /* If signed, separate out the minus */
  62.   if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
  63.     minus = 1;
  64.     val = (uintmax_t)(-(intmax_t)val);
  65.   }
  66.  
  67.   /* Count the number of digits needed.  This returns zero for 0. */
  68.   tmpval = val;
  69.   while ( tmpval ) {
  70.     tmpval /= base;
  71.     ndigits++;
  72.   }
  73.  
  74.   /* Adjust ndigits for size of output */
  75.  
  76.   if ( flags & FL_HASH && base == 8 ) {
  77.     if ( prec < ndigits+1 )
  78.       prec = ndigits+1;
  79.   }
  80.  
  81.   if ( ndigits < prec ) {
  82.     ndigits = prec;        /* Mandatory number padding */
  83.   } else if ( val == 0 ) {
  84.     ndigits = 1;        /* Zero still requires space */
  85.   }
  86.  
  87.   /* For ', figure out what the skip should be */
  88.   if ( flags & FL_TICK ) {
  89.     tickskip = (base == 16) ? 4 : 3;
  90.   } else {
  91.     tickskip = ndigits;        /* No tick marks */
  92.   }
  93.  
  94.   /* Tick marks aren't digits, but generated by the number converter */
  95.   ndigits += (ndigits-1)/tickskip;
  96.  
  97.   /* Now compute the number of nondigits */
  98.   nchars = ndigits;
  99.  
  100.   if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
  101.     nchars++;            /* Need space for sign */
  102.   if ( (flags & FL_HASH) && base == 16 ) {
  103.     nchars += 2;        /* Add 0x for hex */
  104.   }
  105.  
  106.   /* Emit early space padding */
  107.   if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
  108.     while ( width > nchars ) {
  109.       EMIT(' ');
  110.       width--;
  111.     }
  112.   }
  113.  
  114.   /* Emit nondigits */
  115.   if ( minus )
  116.     EMIT('-');
  117.   else if ( flags & FL_PLUS )
  118.     EMIT('+');
  119.   else if ( flags & FL_SPACE )
  120.     EMIT(' ');
  121.  
  122.   if ( (flags & FL_HASH) && base == 16 ) {
  123.     EMIT('0');
  124.     EMIT((flags & FL_UPPER) ? 'X' : 'x');
  125.   }
  126.  
  127.   /* Emit zero padding */
  128.   if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
  129.     while ( width > nchars ) {
  130.       EMIT('0');
  131.       width--;
  132.     }
  133.   }
  134.  
  135.   /* Generate the number.  This is done from right to left. */
  136.   q += ndigits;            /* Advance the pointer to end of number */
  137.   o += ndigits;
  138.   qq = q; oo = o;        /* Temporary values */
  139.  
  140.   b4tick = tickskip;
  141.   while ( ndigits > 0 ) {
  142.     if ( !b4tick-- ) {
  143.       qq--; oo--; ndigits--;
  144.       if ( oo < n ) *qq = '_';
  145.       b4tick = tickskip-1;
  146.     }
  147.     qq--; oo--; ndigits--;
  148.     if ( oo < n ) *qq = digits[val%base];
  149.     val /= base;
  150.   }
  151.  
  152.   /* Emit late space padding */
  153.   while ( (flags & FL_MINUS) && width > nchars ) {
  154.     EMIT(' ');
  155.     width--;
  156.   }
  157.  
  158.   return o;
  159. }
  160.  
  161.  
  162. int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
  163. {
  164.   const char *p = format;
  165.   char ch;
  166.   char *q = buffer;
  167.   size_t o = 0;            /* Number of characters output */
  168.   uintmax_t val = 0;
  169.   int rank = rank_int;        /* Default rank */
  170.   int width = 0;
  171.   int prec  = -1;
  172.   int base;
  173.   size_t sz;
  174.   enum flags flags = 0;
  175.   enum {
  176.     st_normal,            /* Ground state */
  177.     st_flags,            /* Special flags */
  178.     st_width,            /* Field width */
  179.     st_prec,            /* Field precision */
  180.     st_modifiers        /* Length or conversion modifiers */
  181.   } state = st_normal;
  182.   const char *sarg;        /* %s string argument */
  183.   char carg;            /* %c char argument */
  184.   int slen;            /* String length */
  185.  
  186.   while ( (ch = *p++) ) {
  187.     switch ( state ) {
  188.     case st_normal:
  189.       if ( ch == '%' ) {
  190.     state = st_flags;
  191.     flags = 0; rank = rank_int; width = 0; prec = -1;
  192.       } else {
  193.     EMIT(ch);
  194.       }
  195.       break;
  196.  
  197.     case st_flags:
  198.       switch ( ch ) {
  199.       case '-':
  200.     flags |= FL_MINUS;
  201.     break;
  202.       case '+':
  203.     flags |= FL_PLUS;
  204.     break;
  205.       case '\'':
  206.     flags |= FL_TICK;
  207.     break;
  208.       case ' ':
  209.     flags |= FL_SPACE;
  210.     break;
  211.       case '#':
  212.     flags |= FL_HASH;
  213.     break;
  214.       case '0':
  215.     flags |= FL_ZERO;
  216.     break;
  217.       default:
  218.     state = st_width;
  219.     p--;            /* Process this character again */
  220.     break;
  221.       }
  222.       break;
  223.  
  224.     case st_width:
  225.       if ( ch >= '0' && ch <= '9' ) {
  226.     width = width*10+(ch-'0');
  227.       } else if ( ch == '*' ) {
  228.     width = va_arg(ap, int);
  229.     if ( width < 0 ) {
  230.       width = -width;
  231.       flags |= FL_MINUS;
  232.     }
  233.       } else if ( ch == '.' ) {
  234.     prec = 0;        /* Precision given */
  235.     state = st_prec;
  236.       } else {
  237.     state = st_modifiers;
  238.     p--;            /* Process this character again */
  239.       }
  240.       break;
  241.  
  242.     case st_prec:
  243.       if ( ch >= '0' && ch <= '9' ) {
  244.     prec = prec*10+(ch-'0');
  245.       } else if ( ch == '*' ) {
  246.     prec = va_arg(ap, int);
  247.     if ( prec < 0 )
  248.       prec = -1;
  249.       } else {
  250.     state = st_modifiers;
  251.     p--;            /* Process this character again */
  252.       }
  253.       break;
  254.  
  255.     case st_modifiers:
  256.       switch ( ch ) {
  257.     /* Length modifiers - nonterminal sequences */
  258.       case 'h':
  259.     rank--;            /* Shorter rank */
  260.     break;
  261.       case 'l':
  262.     rank++;            /* Longer rank */
  263.     break;
  264.       case 'j':
  265.     rank = INTMAX_RANK;
  266.     break;
  267.       case 'z':
  268.     rank = SIZE_T_RANK;
  269.     break;
  270.       case 't':
  271.     rank = PTRDIFF_T_RANK;
  272.     break;
  273.       case 'L':
  274.       case 'q':
  275.     rank += 2;
  276.     break;
  277.       default:
  278.     /* Output modifiers - terminal sequences */
  279.     state = st_normal;    /* Next state will be normal */
  280.     if ( rank < MIN_RANK )    /* Canonicalize rank */
  281.       rank = MIN_RANK;
  282.     else if ( rank > MAX_RANK )
  283.       rank = MAX_RANK;
  284.  
  285.     switch ( ch ) {
  286.     case 'P':        /* Upper case pointer */
  287.       flags |= FL_UPPER;
  288.       /* fall through */
  289.     case 'p':        /* Pointer */
  290.       base = 16;
  291.       prec = (CHAR_BIT*sizeof(void *)+3)/4;
  292.       flags |= FL_HASH;
  293.       val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
  294.       goto is_integer;
  295.  
  296.     case 'd':        /* Signed decimal output */
  297.     case 'i':
  298.       base = 10;
  299.       flags |= FL_SIGNED;
  300.       switch (rank) {
  301.       case rank_char:
  302.         /* Yes, all these casts are needed... */
  303.         val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
  304.         break;
  305.       case rank_short:
  306.         val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
  307.         break;
  308.       case rank_int:
  309.         val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
  310.         break;
  311.       case rank_long:
  312.         val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
  313.         break;
  314.       case rank_longlong:
  315.         val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
  316.         break;
  317.       }
  318.       goto is_integer;
  319.     case 'o':        /* Octal */
  320.       base = 8;
  321.       goto is_unsigned;
  322.     case 'u':        /* Unsigned decimal */
  323.       base = 10;
  324.       goto is_unsigned;
  325.     case 'X':        /* Upper case hexadecimal */
  326.       flags |= FL_UPPER;
  327.       /* fall through */
  328.     case 'x':        /* Hexadecimal */
  329.       base = 16;
  330.       goto is_unsigned;
  331.  
  332.     is_unsigned:
  333.       switch (rank) {
  334.       case rank_char:
  335.         val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
  336.         break;
  337.       case rank_short:
  338.         val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
  339.         break;
  340.       case rank_int:
  341.         val = (uintmax_t)va_arg(ap, unsigned int);
  342.         break;
  343.       case rank_long:
  344.         val = (uintmax_t)va_arg(ap, unsigned long);
  345.         break;
  346.       case rank_longlong:
  347.         val = (uintmax_t)va_arg(ap, unsigned long long);
  348.         break;
  349.       }
  350.       /* fall through */
  351.  
  352.     is_integer:
  353.       sz = format_int(q, (o<n) ? n-o : 0, val, flags, base, width, prec);
  354.       q += sz; o += sz;
  355.       break;
  356.  
  357.     case 'c':        /* Character */
  358.       carg = (char)va_arg(ap, int);
  359.       sarg = &carg;
  360.       slen = 1;
  361.       goto is_string;
  362.     case 's':        /* String */
  363.       sarg = va_arg(ap, const char *);
  364.       sarg = sarg ? sarg : "(null)";
  365.       slen = strlen(sarg);
  366.       goto is_string;
  367.  
  368.     is_string:
  369.       {
  370.         char sch;
  371.         int i;
  372.         
  373.         if ( prec != -1 && slen > prec )
  374.           slen = prec;
  375.         
  376.         if ( width > slen && !(flags & FL_MINUS) ) {
  377.           char pad = (flags & FL_ZERO) ? '0' : ' ';
  378.           while ( width > slen ) {
  379.         EMIT(pad);
  380.         width--;
  381.           }
  382.         }
  383.         for ( i = slen ; i ; i-- ) {
  384.           sch = *sarg++;
  385.           EMIT(sch);
  386.         }
  387.         if ( width > slen && (flags & FL_MINUS) ) {
  388.           while ( width > slen ) {
  389.         EMIT(' ');
  390.         width--;
  391.           }
  392.         }
  393.       }
  394.       break;
  395.  
  396.     case 'n':        /* Output the number of characters written */
  397.       {
  398.         switch (rank) {
  399.         case rank_char:
  400.           *va_arg(ap, signed char *) = o;
  401.           break;
  402.         case rank_short:
  403.           *va_arg(ap, signed short *) = o;
  404.           break;
  405.         case rank_int:
  406.           *va_arg(ap, signed int *) = o;
  407.           break;
  408.         case rank_long:
  409.           *va_arg(ap, signed long *) = o;
  410.           break;
  411.         case rank_longlong:
  412.           *va_arg(ap, signed long long *) = o;
  413.           break;
  414.         }
  415.       }
  416.       break;
  417.       
  418.     default:        /* Anything else, including % */
  419.       EMIT(ch);
  420.       break;
  421.     }
  422.       }
  423.     }
  424.   }
  425.  
  426.   /* Null-terminate the string */
  427.   if ( o<n )
  428.     *q = '\0';            /* No overflow */
  429.   else if ( n>0 )
  430.     buffer[n-1] = '\0';        /* Overflow - terminate at end of buffer */
  431.  
  432.   return o;
  433. }
  434.